﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.Xml;
using System.Data.SQLite;
using System.IO;

namespace Stone
{
    public class Module_Gps_Osm : Module_Gps
    {
        //http://home.online.no/~sigurdhu/Deg_formats.htm



        //Double buffering
        public Image ImageToPaint = null;

        //Variables
        private string txtPosition = "";
        public TimeSpan LastRenderingTime = new TimeSpan();
        private DB dbStatistics = new DB(Settings.DB_Statistics_Path);

        //GPGSV gpgsv = new GPGSV();
        List<GPGSV.Satellite> satellites = new List<GPGSV.Satellite>();
        int visibleSatellites = 0;
        //Variables MAP
        private OSM_Map osmMap = new OSM_Map();
        //Variables Colors
        public static Color Color_Map_CAR = Color.FromArgb(226, 75, 35);
        public static Color Color_Map_Background = Color.FromArgb(241, 238, 232);
        public static Color Color_Map_Buildings = Color.FromArgb(204, 153, 153);
        public static Color Color_Map_Roads_Default = Color.FromArgb(165, 0, 175);
        //Variables Pens
        public static Pen Pen_Map_CAR = new Pen(Color_Map_CAR, 5);
        public static Pen Pen_Map_Buildings = new Pen(Color_Map_Buildings, 5);
        public static Pen Pen_Map_Roads = new Pen(Color_Map_Roads_Default, 5);
        //Variables Brushes
        Brush Brush_Map_Background = new SolidBrush(Color_Map_Background);
        Brush Brush_Map_Buildings = new SolidBrush(Color_Map_Buildings);

        //Callbacks
        delegate void SetLabelPositionTextCallback(string text);
        delegate void CallRepaintCallback();

        //Constructor
        public Module_Gps_Osm()
        {
            dbStatistics.Connect();
        }

        //Methods
        public void PaintViewport_new(Panel panelViewport)
        {
            DateTime RenderingStartTime = DateTime.Now;

            //Creating layer images to draw
            Bitmap rotatingImage = new Bitmap(panelViewport.Width, panelViewport.Height);
            Graphics graphicsOfRotatingImage = Graphics.FromImage(rotatingImage);
            Bitmap fixedImage = new Bitmap(panelViewport.Width, panelViewport.Height);
            Graphics graphicsOfFixedImage = Graphics.FromImage(fixedImage); 

            //Coordinates window      
            float diff_lat = osmMap.bounds_world_max_lat - osmMap.bounds_world_min_lat;
            float diff_lon = osmMap.bounds_world_max_lon - osmMap.bounds_world_min_lon;
            float world_Width = osmMap.bounds_world_max_lon - osmMap.bounds_world_min_lon;
            float world_Height = osmMap.bounds_world_max_lat - osmMap.bounds_world_min_lat;
            int screen_Height = panelViewport.Height;
            int screen_Width = panelViewport.Width;

            //CAR coordinates
            float CAR_point_world_X = osmMap.bounds_world_max_lon - osmMap.CAR_position.X;
            float CAR_point_world_Y = osmMap.bounds_world_max_lat - osmMap.CAR_position.Y;
            int CAR_point_screen_X = screen_Width - (int)(screen_Width * CAR_point_world_X / world_Width);
            int CAR_point_screen_Y = (int)(screen_Height * CAR_point_world_Y / world_Height);

            graphicsOfRotatingImage.SmoothingMode = SmoothingMode.AntiAlias;
            graphicsOfRotatingImage.PixelOffsetMode = PixelOffsetMode.HighQuality;

            float direction = (float)osmMap.CAR_course;
            graphicsOfRotatingImage.TranslateTransform((float)CAR_point_screen_X, (float)CAR_point_screen_Y);
            graphicsOfRotatingImage.RotateTransform(-direction);
            graphicsOfRotatingImage.TranslateTransform(-(float)CAR_point_screen_X, -(float)CAR_point_screen_Y);

            //Background
            graphicsOfRotatingImage.FillRectangle(Brush_Map_Background, 0, 0, panelViewport.Width, panelViewport.Height);

            //X_db : X_panel = maxX_real : Width
            //X_panel = X_db * Width / maxX_real

            foreach (OSM_Way tmpWay in osmMap.GetWaysInView())
            {
                bool first = true;
                int previousNode_X = 0;
                int previousNode_Y = 0;

                Pen pen = Pen_Map_Roads;

                if (tmpWay.wayType == "motorway") pen = new Pen(Color.FromArgb(128, 155, 192), 8);
                if (tmpWay.wayType == "motorway_link") pen = new Pen(Color.FromArgb(128, 155, 192), 8);
                if (tmpWay.wayType == "trunk") pen = new Pen(Color.FromArgb(127, 201, 127), 7);
                if (tmpWay.wayType == "trunk_link") pen = new Pen(Color.FromArgb(127, 201, 127), 7);
                if (tmpWay.wayType == "primary") pen = new Pen(Color.FromArgb(228, 109, 113), 6);
                if (tmpWay.wayType == "primary_link") pen = new Pen(Color.FromArgb(228, 109, 113), 6);
                if (tmpWay.wayType == "secondary") pen = new Pen(Color.FromArgb(253, 191, 111), 5);
                if (tmpWay.wayType == "secondary_link") pen = new Pen(Color.FromArgb(253, 191, 111), 5);
                if (tmpWay.wayType == "tertiary") pen = new Pen(Color.FromArgb(252, 250, 116), 4);
                if (tmpWay.wayType == "residential") pen = new Pen(Color.FromArgb(203, 203, 203), 3);
                if (tmpWay.wayType == "unclassified") pen = new Pen(Color.FromArgb(203, 203, 203), 3);
                if (tmpWay.wayType == "road") pen = new Pen(Color.FromArgb(203, 203, 203), 3);
                if (tmpWay.wayType == "service") pen = new Pen(Color.FromArgb(253, 191, 111), 3);
                if (tmpWay.wayType == "footway") pen = new Pen(Color.FromArgb(234, 229, 157), 3);

                //if (tmpWay.wayType == "service") pen = new Pen(Color.Blue, 5);                    
                //if (tmpWay.wayType == "footway") pen = new Pen(Color.Green, 5);

                foreach (OSM_Node tmpNode in tmpWay.nodes)
                {
                    float point_world_X = osmMap.bounds_world_max_lon - tmpNode.LON;
                    float point_world_Y = osmMap.bounds_world_max_lat - tmpNode.LAT;

                    //PointF pointTransformed = GlobalFunctions.rotatePoint(osmMap.CAR_position, new PointF(point_world_X, point_world_Y), (float)osmMap.CAR_course);

                    //point_world_X = pointTransformed.X;
                    //point_world_Y = pointTransformed.Y;

                    int point_screen_X = 0;
                    int point_screen_Y = 0;

                    // world_Width : screen_Width = point_world_X : point_screen_X
                    point_screen_X = screen_Width - (int)(screen_Width * point_world_X / world_Width);
                    // world_Height : screen_Height = point_world_Y : point_screen_Y
                    point_screen_Y = (int)(screen_Height * point_world_Y / world_Height);

                    if ((point_screen_X >= 0) && (point_screen_Y >= 0))
                        if (!first)
                        {
                            graphicsOfRotatingImage.DrawLine(pen, previousNode_X, previousNode_Y, point_screen_X, point_screen_Y);
                            //graphicsOfImage.DrawBezier(pen, new Point(4, 7), new Point(44, 9), new Point(39, 72), new Point(13, 27));
                        }

                    first = false;

                    previousNode_X = point_screen_X;
                    previousNode_Y = point_screen_Y;
                }
            }

            if (osmMap.ViewBuildings)
                foreach (OSM_Building tmpBuilding in osmMap.GetBuildingsInView())
                {
                    int previousNode_X = 0;
                    int previousNode_Y = 0;

                    Point[] pointsBuilding = new Point[tmpBuilding.nodes.Count];
                    int nodesCounter = 0;
                    GraphicsPath pathBuilding = new GraphicsPath();
                    pathBuilding.StartFigure();

                    foreach (OSM_Node tmpNode in tmpBuilding.nodes)
                    {
                        float point_world_X = osmMap.bounds_world_max_lon - tmpNode.LON;
                        float point_world_Y = osmMap.bounds_world_max_lat - tmpNode.LAT;
                        int point_screen_X = 0;
                        int point_screen_Y = 0;

                        // world_Width : screen_Width = point_world_X : point_screen_X
                        point_screen_X = screen_Width - (int)(screen_Width * point_world_X / world_Width);
                        // world_Height : screen_Height = point_world_Y : point_screen_Y
                        point_screen_Y = (int)(screen_Height * point_world_Y / world_Height);

                        if ((point_screen_X >= 0) && (point_screen_Y >= 0))
                            pointsBuilding[nodesCounter] = new Point(point_screen_X, point_screen_Y);

                        nodesCounter++;

                        previousNode_X = point_screen_X;
                        previousNode_Y = point_screen_Y;
                    }

                    if (pointsBuilding.Length > 0)
                    {
                        Point firstPoint = pointsBuilding.First();
                        Point LastPoint = pointsBuilding.Last();

                        if ((firstPoint.X == LastPoint.X) && (firstPoint.Y == LastPoint.Y))
                            graphicsOfRotatingImage.FillPolygon(Brush_Map_Buildings, pointsBuilding);
                    }
                }

            //CAR

            string pathCar2dImageFile = Settings.Skins_Path + "\\" + Settings.Skin_Name + "\\Gps\\car.png";

            if (File.Exists(pathCar2dImageFile))
            {
                Image carImage = Bitmap.FromFile(pathCar2dImageFile);

                int carImage_Position_X = CAR_point_screen_X - (carImage.Width / 2);
                int carImage_Position_Y = CAR_point_screen_Y - (carImage.Height / 2);

                graphicsOfRotatingImage.DrawImage(carImage, carImage_Position_X, carImage_Position_Y);
            }

            graphicsOfRotatingImage.DrawEllipse(Pen_Map_CAR, CAR_point_screen_X, CAR_point_screen_Y, 5, 5);

            graphicsOfRotatingImage.DrawString(osmMap.GetCarPositionString(), new System.Drawing.Font("Arial", 8), Brushes.Red, new Point(CAR_point_screen_X + 20, CAR_point_screen_Y - 14));
            graphicsOfRotatingImage.DrawString(osmMap.GetCarSpeedInKilometersPerHour() + " Km/h", new System.Drawing.Font("Arial", 8), Brushes.Red, new Point(CAR_point_screen_X + 20, CAR_point_screen_Y));
            graphicsOfRotatingImage.DrawString("Direction " + osmMap.CAR_course + "°", new System.Drawing.Font("Arial", 8), Brushes.Red, new Point(CAR_point_screen_X + 20, CAR_point_screen_Y + 14));


            //Satellites in view
            graphicsOfRotatingImage.DrawString("Satellites in view: " + visibleSatellites, new System.Drawing.Font("Arial", 8), Brushes.Gray, new PointF(50f, 24f));

            float shifter = 0f;

            foreach (GPGSV.Satellite tmp in satellites)
            {
                string txtSatellite = "(" + tmp.PRN + ") " + " \tElevation:" + tmp.Elevation + " \tPosition:" + tmp.Azimuth + " \tSignal:" + tmp.SNR;
                graphicsOfRotatingImage.DrawString(txtSatellite, new System.Drawing.Font("Arial", 8), Brushes.Gray, new PointF(180f, 24f + shifter));
                shifter += 14f;
            }


            //Tachimeter
            graphicsOfRotatingImage.DrawString(String.Format("{0:0.}", osmMap.GetCarSpeedInKilometersPerHour()) + " Km/h", new System.Drawing.Font("Arial", 24), Brushes.Blue, new Point(630, 350));

            //Where Am I
            if (null != osmMap.WhereAmI())
                graphicsOfRotatingImage.DrawString(osmMap.WhereAmI().name, new System.Drawing.Font("Arial", 12), Brushes.Blue, new Point(50, 360));




            //Image viewPortImageRotated = GlobalFunctions.RotateImage(viewportImage, (float)osmMap.CAR_course);
            



            //Render Image
            //graphics.DrawImage(viewportImage, panelGpsViewport.rectangle.Left, panelGpsViewport.rectangle.Top, panelGpsViewport.rectangle, GraphicsUnit.Pixel);

            LastRenderingTime = DateTime.Now - RenderingStartTime;

            ImageToPaint = rotatingImage;
        }

        public void PaintViewport(Skin_Panel panelGpsViewport, Graphics graphics)
        {
            DateTime RenderingStartTime = DateTime.Now;
            Image viewportImage = new Bitmap(panelGpsViewport.rectangle.Width, panelGpsViewport.rectangle.Height);
            Graphics graphicsOfImage = Graphics.FromImage(viewportImage);

            // Latitudine N, Longitudine EST
            //  <bounds minlat="45.4877200" minlon="9.2318900" maxlat="45.4940100" maxlon="9.2427400"/>

            //Coordinates window      
            float diff_lat = osmMap.bounds_world_max_lat - osmMap.bounds_world_min_lat;
            float diff_lon = osmMap.bounds_world_max_lon - osmMap.bounds_world_min_lon;
            float world_Width = osmMap.bounds_world_max_lon - osmMap.bounds_world_min_lon;
            float world_Height = osmMap.bounds_world_max_lat - osmMap.bounds_world_min_lat;
            int screen_Height = panelGpsViewport.rectangle.Height;
            int screen_Width = panelGpsViewport.rectangle.Width;

            graphicsOfImage.SmoothingMode = SmoothingMode.AntiAlias;
            graphicsOfImage.PixelOffsetMode = PixelOffsetMode.HighQuality;

            //Background
            graphicsOfImage.FillRectangle(Brush_Map_Background, panelGpsViewport.rectangle);

            //X_db : X_panel = maxX_real : Width
            //X_panel = X_db * Width / maxX_real

            foreach (OSM_Way tmpWay in osmMap.GetWaysInView())
            {
                bool first = true;
                int previousNode_X = 0;
                int previousNode_Y = 0;

                Pen pen = Pen_Map_Roads;

                if (tmpWay.wayType == "motorway") pen = new Pen(Color.FromArgb(128, 155, 192), 8);
                if (tmpWay.wayType == "motorway_link") pen = new Pen(Color.FromArgb(128, 155, 192), 8);
                if (tmpWay.wayType == "trunk") pen = new Pen(Color.FromArgb(127, 201, 127), 7);
                if (tmpWay.wayType == "trunk_link") pen = new Pen(Color.FromArgb(127, 201, 127), 7);
                if (tmpWay.wayType == "primary") pen = new Pen(Color.FromArgb(228, 109, 113), 6);
                if (tmpWay.wayType == "primary_link") pen = new Pen(Color.FromArgb(228, 109, 113), 6);
                if (tmpWay.wayType == "secondary") pen = new Pen(Color.FromArgb(253, 191, 111), 5);
                if (tmpWay.wayType == "secondary_link") pen = new Pen(Color.FromArgb(253, 191, 111), 5);
                if (tmpWay.wayType == "tertiary") pen = new Pen(Color.FromArgb(252, 250, 116), 4);
                if (tmpWay.wayType == "residential") pen = new Pen(Color.FromArgb(203, 203, 203), 3);
                if (tmpWay.wayType == "unclassified") pen = new Pen(Color.FromArgb(203, 203, 203), 3);
                if (tmpWay.wayType == "road") pen = new Pen(Color.FromArgb(203, 203, 203), 3);
                if (tmpWay.wayType == "service") pen = new Pen(Color.FromArgb(253, 191, 111), 3);
                if (tmpWay.wayType == "footway") pen = new Pen(Color.FromArgb(234, 229, 157), 3);

                //if (tmpWay.wayType == "service") pen = new Pen(Color.Blue, 5);                    
                //if (tmpWay.wayType == "footway") pen = new Pen(Color.Green, 5);

                foreach (OSM_Node tmpNode in tmpWay.nodes)
                {
                    float point_world_X = osmMap.bounds_world_max_lon - tmpNode.LON;
                    float point_world_Y = osmMap.bounds_world_max_lat - tmpNode.LAT;

                    //PointF pointTransformed = GlobalFunctions.rotatePoint(osmMap.CAR_position, new PointF(point_world_X, point_world_Y), (float)osmMap.CAR_course);

                    //point_world_X = pointTransformed.X;
                    //point_world_Y = pointTransformed.Y;

                    int point_screen_X = 0;
                    int point_screen_Y = 0;

                    // world_Width : screen_Width = point_world_X : point_screen_X
                    point_screen_X = screen_Width - (int)(screen_Width * point_world_X / world_Width);
                    // world_Height : screen_Height = point_world_Y : point_screen_Y
                    point_screen_Y = (int)(screen_Height * point_world_Y / world_Height);

                    if ((point_screen_X >= 0) && (point_screen_Y >= 0))
                        if (!first)
                        {
                            graphicsOfImage.DrawLine(pen, previousNode_X, previousNode_Y, point_screen_X, point_screen_Y);
                            //graphicsOfImage.DrawBezier(pen, new Point(4, 7), new Point(44, 9), new Point(39, 72), new Point(13, 27));
                        }

                    first = false;

                    previousNode_X = point_screen_X;
                    previousNode_Y = point_screen_Y;
                }
            }

            foreach (OSM_Building tmpBuilding in osmMap.GetBuildingsInView())
            {
                int previousNode_X = 0;
                int previousNode_Y = 0;

                Point[] pointsBuilding = new Point[tmpBuilding.nodes.Count];
                int nodesCounter = 0;
                GraphicsPath pathBuilding = new GraphicsPath();
                pathBuilding.StartFigure();

                foreach (OSM_Node tmpNode in tmpBuilding.nodes)
                {
                    float point_world_X = osmMap.bounds_world_max_lon - tmpNode.LON;
                    float point_world_Y = osmMap.bounds_world_max_lat - tmpNode.LAT;
                    int point_screen_X = 0;
                    int point_screen_Y = 0;

                    // world_Width : screen_Width = point_world_X : point_screen_X
                    point_screen_X = screen_Width - (int)(screen_Width * point_world_X / world_Width);
                    // world_Height : screen_Height = point_world_Y : point_screen_Y
                    point_screen_Y = (int)(screen_Height * point_world_Y / world_Height);

                    if ((point_screen_X >= 0) && (point_screen_Y >= 0))
                        pointsBuilding[nodesCounter] = new Point(point_screen_X, point_screen_Y);

                    nodesCounter++;

                    previousNode_X = point_screen_X;
                    previousNode_Y = point_screen_Y;
                }

                if (pointsBuilding.Length > 0)
                {
                    Point firstPoint = pointsBuilding.First();
                    Point LastPoint = pointsBuilding.Last();

                    if ((firstPoint.X == LastPoint.X) && (firstPoint.Y == LastPoint.Y))
                        graphicsOfImage.FillPolygon(Brush_Map_Buildings, pointsBuilding);
                }
            }



            //CAR
            float CAR_point_world_X = osmMap.bounds_world_max_lon - osmMap.CAR_position.X;
            float CAR_point_world_Y = osmMap.bounds_world_max_lat - osmMap.CAR_position.Y;
            int CAR_point_screen_X = screen_Width - (int)(screen_Width * CAR_point_world_X / world_Width);
            int CAR_point_screen_Y = (int)(screen_Height * CAR_point_world_Y / world_Height);

            string pathCar2dImageFile = Settings.Skins_Path + "\\" + Settings.Skin_Name + "\\Gps\\car.png";

            if (File.Exists(pathCar2dImageFile))
            {
                Image carImage = Bitmap.FromFile(pathCar2dImageFile);

                int carImage_Position_X = CAR_point_screen_X - (carImage.Width / 2);
                int carImage_Position_Y = CAR_point_screen_Y - (carImage.Height / 2);

                graphicsOfImage.DrawImage(carImage, carImage_Position_X, carImage_Position_Y);
            }

            graphicsOfImage.DrawEllipse(Pen_Map_CAR, CAR_point_screen_X, CAR_point_screen_Y, 5, 5);

            graphicsOfImage.DrawString(osmMap.GetCarPositionString(), new System.Drawing.Font("Arial", 8), Brushes.Red, new Point(CAR_point_screen_X + 20, CAR_point_screen_Y - 14));
            graphicsOfImage.DrawString(osmMap.GetCarSpeedInKilometersPerHour() + " Km/h", new System.Drawing.Font("Arial", 8), Brushes.Red, new Point(CAR_point_screen_X + 20, CAR_point_screen_Y));
            graphicsOfImage.DrawString("Direction " + osmMap.CAR_course + "°", new System.Drawing.Font("Arial", 8), Brushes.Red, new Point(CAR_point_screen_X + 20, CAR_point_screen_Y + 14));


            //Satellites in view
            graphicsOfImage.DrawString("Satellites in view: " + visibleSatellites, new System.Drawing.Font("Arial", 8), Brushes.Gray, new PointF(50f, 24f));

            float shifter = 0f;

            foreach (GPGSV.Satellite tmp in satellites)
            {
                string txtSatellite = "(" + tmp.PRN + ") " + " \tElevation:" + tmp.Elevation + " \tPosition:" + tmp.Azimuth + " \tSignal:" + tmp.SNR;
                graphicsOfImage.DrawString(txtSatellite, new System.Drawing.Font("Arial", 8), Brushes.Gray, new PointF(180f, 24f + shifter));
                shifter += 14f;
            }


            //Tachimeter
            graphicsOfImage.DrawString(osmMap.GetCarSpeedInKilometersPerHour() + " Km/h", new System.Drawing.Font("Arial", 24), Brushes.Blue, new Point(630, 350));


            //Image viewPortImageRotated = GlobalFunctions.RotateImage(viewportImage, (float)osmMap.CAR_course);

            graphicsOfImage.RotateTransform((float)osmMap.CAR_course);



            //Render Image
            graphics.DrawImage(viewportImage, panelGpsViewport.rectangle.Left, panelGpsViewport.rectangle.Top, panelGpsViewport.rectangle, GraphicsUnit.Pixel);
            LastRenderingTime = DateTime.Now - RenderingStartTime;

        }
        private void SetLabelPosition(string text)
        {
            txtPosition = text;
        }
        //private void LoadData_OLD(float bounds_world_min_lat, float bounds_world_min_lon, float bounds_world_max_lat, float bounds_world_max_lon)
        //{
        //    if (!DrawingMap)
        //        if (osmMap.bounds_world_min_lat != bounds_world_min_lat)
        //            if (osmMap.bounds_world_min_lon != bounds_world_min_lon)
        //                if (osmMap.bounds_world_max_lat != bounds_world_max_lat)
        //                    if (osmMap.bounds_world_max_lon != bounds_world_max_lon)
        //                    {
        //                        DateTime StartLoadingDataFromDbTime = DateTime.Now;
        //                        LoadingDataFromDB = true;
        //                        osmMap = new OSM_Map();

        //                        osmMap.bounds_world_min_lat = bounds_world_min_lat;
        //                        osmMap.bounds_world_min_lon = bounds_world_min_lon;
        //                        osmMap.bounds_world_max_lat = bounds_world_max_lat;
        //                        osmMap.bounds_world_max_lon = bounds_world_max_lon;

        //                        db.Connect();

        //                        //***********  WAYS  **********************

        //                        string queryWays = "SELECT way_nodes.way AS id_way, nodes.id AS id_node, num, nodes.lon, nodes.lat, way_tags.value AS name ";
        //                        queryWays += "FROM nodes INNER JOIN way_nodes ON nodes.id = way_nodes.node INNER JOIN way_tags ON way_nodes.way = way_tags.ref ";
        //                        queryWays += "WHERE nodes.lon >= '" + bounds_world_min_lon.ToString().Replace(",", ".") + "' ";
        //                        queryWays += "AND nodes.lon <= '" + bounds_world_max_lon.ToString().Replace(",", ".") + "' ";
        //                        queryWays += "AND nodes.lat >= '" + bounds_world_min_lat.ToString().Replace(",", ".") + "' ";
        //                        queryWays += "AND nodes.lat <= '" + bounds_world_max_lat.ToString().Replace(",", ".") + "' ";
        //                        queryWays += "AND  way_tags.key = 'highway' ";
        //                        queryWays += "GROUP BY way_nodes.way";

        //                        DataTable resultDataTableWays = db.ExecuteQuery(queryWays);

        //                        foreach (DataRow tmp in resultDataTableWays.Rows)
        //                        {
        //                            OSM_Way newWay = new OSM_Way();
        //                            long.TryParse(tmp["id_way"].ToString(), out newWay.id);
        //                            newWay.name = tmp["name"].ToString();

        //                            osmMap.ways.Add(newWay);
        //                        }

        //                        foreach (OSM_Way tmpWay in osmMap.ways)
        //                        {
        //                            string queryTypes = "SELECT * FROM way_tags where key = 'highway' AND ref = '" + tmpWay.id + "'";
        //                            DataTable resultDataTableTypes = db.ExecuteQuery(queryTypes);

        //                            foreach (DataRow tmp in resultDataTableTypes.Rows)
        //                            {
        //                                tmpWay.wayType = tmp["value"].ToString();
        //                            }
        //                        }

        //                        foreach (OSM_Way tmpWay in osmMap.ways)
        //                        {
        //                            string queryNodes = "SELECT * FROM nodes INNER JOIN way_nodes ON nodes.id = way_nodes.node WHERE way_nodes.way = '" + tmpWay.id + "' group by num";
        //                            DataTable resultDataTableNodes = db.ExecuteQuery(queryNodes);

        //                            foreach (DataRow tmp in resultDataTableNodes.Rows)
        //                            {
        //                                float floatLon = 0f;
        //                                float.TryParse(tmp["lon"].ToString(), out floatLon);
        //                                float floatLat = 0f;
        //                                float.TryParse(tmp["lat"].ToString(), out floatLat);
        //                                OSM_Node newNode = new OSM_Node();
        //                                long.TryParse(tmp["id"].ToString(), out newNode.id);
        //                                newNode.LON = floatLon;
        //                                newNode.LAT = floatLat;

        //                                tmpWay.nodes.Add(newNode);
        //                            }
        //                        }


        //                        //***********  BUILDINGS  **********************

        //                        //string queryBuildings = "SELECT way_nodes.way AS id_way, nodes.id AS id_node, num, nodes.lon, nodes.lat, way_tags.value AS name ";
        //                        //queryBuildings += "FROM nodes INNER JOIN way_nodes ON nodes.id = way_nodes.node INNER JOIN way_tags ON way_nodes.way = way_tags.ref ";
        //                        //queryBuildings += "WHERE nodes.lon >= '" + bounds_world_min_lon.ToString().Replace(",", ".") + "' ";
        //                        //queryBuildings += "AND nodes.lon <= '" + bounds_world_max_lon.ToString().Replace(",", ".") + "' ";
        //                        //queryBuildings += "AND nodes.lat >= '" + bounds_world_min_lat.ToString().Replace(",", ".") + "' ";
        //                        //queryBuildings += "AND nodes.lat <= '" + bounds_world_max_lat.ToString().Replace(",", ".") + "' ";
        //                        //queryBuildings += "AND  way_tags.key = 'building' ";
        //                        //queryBuildings += "GROUP BY way_nodes.way";

        //                        //DataTable resultDataTableBuildings = db.ExecuteQuery(queryBuildings);

        //                        //foreach (DataRow tmp in resultDataTableBuildings.Rows)
        //                        //{
        //                        //    OSM_Building newBuilding = new OSM_Building();
        //                        //    long.TryParse(tmp["id_way"].ToString(), out newBuilding.id);

        //                        //    osmMap.buildings.Add(newBuilding);
        //                        //}

        //                        //foreach (OSM_Building tmpBuilding in osmMap.buildings)
        //                        //{
        //                        //    string queryNodes = "SELECT * FROM nodes INNER JOIN way_nodes ON nodes.id = way_nodes.node WHERE way_nodes.way = '" + tmpBuilding.id + "' group by num";
        //                        //    DataTable resultDataTableNodesBuildings = db.ExecuteQuery(queryNodes);

        //                        //    foreach (DataRow tmp in resultDataTableNodesBuildings.Rows)
        //                        //    {
        //                        //        float floatLon = 0f;
        //                        //        float.TryParse(tmp["lon"].ToString(), out floatLon);
        //                        //        float floatLat = 0f;
        //                        //        float.TryParse(tmp["lat"].ToString(), out floatLat);
        //                        //        OSM_Node newNode = new OSM_Node();
        //                        //        long.TryParse(tmp["id"].ToString(), out newNode.id);
        //                        //        newNode.LON = floatLon;
        //                        //        newNode.LAT = floatLat;

        //                        //        tmpBuilding.nodes.Add(newNode);
        //                        //    }
        //                        //}

        //                        osmMap.ToRepaint = true;
        //                        LoadingDataFromDB = false;

        //                        LastLoadDataFormDbTime = DateTime.Now - StartLoadingDataFromDbTime;
        //                    }
        //                    else
        //                        osmMap.ToRepaint = false;
        //}        
        public void ReceiveNMEA(GPSHandler GpsHandler, GPSHandler.GPSEventArgs e)
        {
            switch (e.TypeOfEvent)
            {
                case GPSEventType.GPRMC:  //Recommended minimum specific GPS/Transit data
                    if (GpsHandler.HasGPSFix) //Is a GPS fix available?
                    {
                        osmMap.CAR_timeOfFix = GpsHandler.GPRMC.TimeOfFix;
                        osmMap.CAR_course = GpsHandler.GPRMC.Course;

                        string queryInsertSpeedStatistics = "INSERT INTO Speed (value) VALUES ('" + osmMap.GetCarSpeedInKilometersPerHour().ToString().Replace(",", ".") + "')";
                        dbStatistics.ExecuteNonQuery(queryInsertSpeedStatistics);

                        float Lon = (float)GpsHandler.GPRMC.Position.Longitude;
                        float Lat = (float)GpsHandler.GPRMC.Position.Latitude;

                        osmMap.SetCarPosition(new PointF(Lon, Lat));

                        osmMap.CAR_speed = GpsHandler.GPRMC.Speed;
                    }
                    break;

                case GPSEventType.GPGSV:
                    satellites = GpsHandler.GPGSV.Satellites;
                    visibleSatellites = GpsHandler.GPGSV.SatsInView;
                    break;
            }
        }
        public void PointTo(PointF position)
        {
            osmMap.SetCarPosition(position);
        }
        public TimeSpan GetLastLoadDataFromDB()
        {
            return osmMap.LastLoadDataFromDbTime;
        }
        public void Zoom_IN()
        {
            osmMap.Zoom_IN();
        }
        public void Zoom_OUT()
        {
            osmMap.Zoom_OUT();
        }
        public void Move_LEFT()
        {
            //osmMap.Move_LEFT();
        }
        public void Move_RIGHT()
        {
            //osmMap.Move_RIGHT();
        }
        public void Move_UP()
        {
            //osmMap.Move_UP();
        }
        public void Move_DOWN()
        {
            //osmMap.Move_DOWN();
        }
    }

    class OSM_Map
    {
        //Settings
        public bool ViewBuildings = true;
        public bool ViewPoi = true;

        //public float proximityThreshold = 0.00001f;

        //Position
        private OSM_Way currentWay = null;

        //public Semaphore semaphore = new Semaphore(1, 1, "", 

        //private float AspectRatio = 800f / 480f;
        private float zoom_factor_lat = 0.001f;
        private float zoom_factor_lon = 0.001f;
        private DB db = new DB(Settings.DB_Osm_Path);
        public TimeSpan LastLoadDataFromDbTime = new TimeSpan();
        //public bool LoadingDataFormCache = false;
        //public bool ToRepaint = false;

        //public bool semaphore = false;

        private ArrayList ways = new ArrayList();
        private ArrayList buildings = new ArrayList();

        private ArrayList Cache_ways = new ArrayList();
        private ArrayList Cache_buildings = new ArrayList();

        //private float bounds_world_min_lat = 45.46113f; //45.4877200f;
        //private float bounds_world_min_lon = 9.18677f;  //9.2318900f;
        //private float bounds_world_max_lat = 45.46705f; //45.4940100f;
        //private float bounds_world_max_lon = 9.19461f;  //9.2427400f;
        public float bounds_world_min_lat = 45.49009f;
        public float bounds_world_min_lon = 9.234752f;
        public float bounds_world_max_lat = 45.492008f;
        public float bounds_world_max_lon = 9.239199f;

        //private float bounds_step_lat = 0.00191879272f / 2;
        //private float bounds_step_lon = 0.00444698334f / 2;
        private float bounds_step_lat = 0.006f / 2;
        private float bounds_step_lon = 0.012f / 2;

        public PointF CAR_position = new PointF();
        public double CAR_course = 0f;
        public double CAR_speed = 0f;
        public DateTime CAR_timeOfFix = DateTime.MinValue;

        public double GetCarSpeedInKilometersPerHour()
        {
            return GlobalFunctions.Convert_Knots_to_KilometerPerHour(CAR_speed);
        }
        public ArrayList GetWaysInView()
        {
            return ways;
        }
        public ArrayList GetBuildingsInView()
        {
            return buildings;
        }
        public void SetCarPosition(PointF NewCarPosition)
        {
            if (NewCarPosition != CAR_position)
            {
                //Calculationg new map Bounds
                CAR_position = NewCarPosition;

                //float bounds_width = bounds_world_max_lon - bounds_world_min_lon;
                //float bounds_height = bounds_world_max_lat - bounds_world_min_lat;

                //float bounds_ratio = bounds_width / bounds_height;

                bounds_world_min_lat = CAR_position.Y - bounds_step_lat;
                bounds_world_max_lat = CAR_position.Y + bounds_step_lat;
                bounds_world_min_lon = CAR_position.X - bounds_step_lon;
                bounds_world_max_lon = CAR_position.X + bounds_step_lon;

                //LoadingDataFormDB
                LoadDataAsync();
            }
        }
        public string GetCarPositionString()
        {
            string actualLatitude = CAR_position.Y.ToString("0.00000");
            string actualLongitude = CAR_position.X.ToString("0.00000");

            return "LAT " + actualLatitude + " - LON " + actualLongitude;
        }
        private void LoadDataAsync()
        {
            Cache_ways.Clear();
            Cache_buildings.Clear();

            db.Connect();

            //***********  WAYS  **********************

            //SELECT way_nodes.way AS id_way, nodes.id AS id_node, num, nodes.lon, nodes.lat, way_tags.value AS name 
            //FROM nodes INNER JOIN way_nodes ON nodes.id = way_nodes.node INNER JOIN way_tags ON way_nodes.way = way_tags.ref WHERE nodes.lon >= '9.23189' AND nodes.lon <= '9.24274' AND nodes.lat >= '45.48772' AND nodes.lat <= '45.49401' AND  way_tags.key = 'highway' GROUP BY way_nodes.way

            string queryWays = "SELECT * ";
            queryWays += "FROM ways_full ";
            queryWays += "WHERE node_lon >= '" + bounds_world_min_lon.ToString().Replace(",", ".") + "' ";
            queryWays += "AND node_lon <= '" + bounds_world_max_lon.ToString().Replace(",", ".") + "' ";
            queryWays += "AND node_lat >= '" + bounds_world_min_lat.ToString().Replace(",", ".") + "' ";
            queryWays += "AND node_lat <= '" + bounds_world_max_lat.ToString().Replace(",", ".") + "' ";
            queryWays += "AND type = 'highway' ORDER BY id_way, num";

            DateTime StartLoadingDataFromDbTime = DateTime.Now;
            DataTable resultDataTableWays = db.ExecuteQuery(queryWays);
            LastLoadDataFromDbTime = DateTime.Now - StartLoadingDataFromDbTime;

            long lastWayId = -1;
            OSM_Way lastWayCreated = new OSM_Way();

            foreach (DataRow tmp in resultDataTableWays.Rows)
            {
                long.TryParse(tmp["id_way"].ToString(), out lastWayId);

                if (lastWayId != lastWayCreated.id)
                {
                    Cache_ways.Add(lastWayCreated);
                    lastWayCreated = new OSM_Way();
                    lastWayCreated.id = lastWayId;
                    lastWayCreated.name = tmp["name"].ToString();
                }

                lastWayCreated.wayType = tmp["subtype"].ToString();

                float floatLon = 0f;
                float.TryParse(tmp["node_lon"].ToString(), out floatLon);
                float floatLat = 0f;
                float.TryParse(tmp["node_lat"].ToString(), out floatLat);
                OSM_Node newNode = new OSM_Node();
                long.TryParse(tmp["id_node"].ToString(), out newNode.id);
                newNode.LON = floatLon;
                newNode.LAT = floatLat;

                lastWayCreated.nodes.Add(newNode);
            }



            //***********  BUILDINGS  **********************

            if (ViewBuildings)
            {
                string queryBuildings = "SELECT * ";
                queryBuildings += "FROM ways_full ";
                queryBuildings += "WHERE node_lon >= '" + bounds_world_min_lon.ToString().Replace(",", ".") + "' ";
                queryBuildings += "AND node_lon <= '" + bounds_world_max_lon.ToString().Replace(",", ".") + "' ";
                queryBuildings += "AND node_lat >= '" + bounds_world_min_lat.ToString().Replace(",", ".") + "' ";
                queryBuildings += "AND node_lat <= '" + bounds_world_max_lat.ToString().Replace(",", ".") + "' ";
                queryBuildings += "AND type = 'building' ORDER BY id_way, num";

                DataTable resultDataTableBuildings = db.ExecuteQuery(queryBuildings);

                long lastBuildingId = -1;
                OSM_Building lastBuildingCreated = new OSM_Building();

                foreach (DataRow tmp in resultDataTableBuildings.Rows)
                {
                    long.TryParse(tmp["id_way"].ToString(), out lastBuildingId);

                    if (lastBuildingId != lastBuildingCreated.id)
                    {
                        Cache_buildings.Add(lastBuildingCreated);
                        lastBuildingCreated = new OSM_Building();
                        lastBuildingCreated.id = lastBuildingId;
                    }

                    float floatLon = 0f;
                    float.TryParse(tmp["node_lon"].ToString(), out floatLon);
                    float floatLat = 0f;
                    float.TryParse(tmp["node_lat"].ToString(), out floatLat);
                    OSM_Node newNode = new OSM_Node();
                    long.TryParse(tmp["id_node"].ToString(), out newNode.id);
                    newNode.LON = floatLon;
                    newNode.LAT = floatLat;

                    lastBuildingCreated.nodes.Add(newNode);
                }


                ways = Cache_ways;
                buildings = Cache_buildings;

            }



            //***********  POI  **********************

            //if (ViewPoi)
            //{
            //    //SELECT * FROM way_tags WHERE key = 'amenity' and value = 'fuel'
            //    string queryPoi = "SELECT * ";
            //    queryPoi += "FROM ways_full ";
            //    queryPoi += "WHERE node_lon >= '" + bounds_world_min_lon.ToString().Replace(",", ".") + "' ";
            //    queryPoi += "AND node_lon <= '" + bounds_world_max_lon.ToString().Replace(",", ".") + "' ";
            //    queryPoi += "AND node_lat >= '" + bounds_world_min_lat.ToString().Replace(",", ".") + "' ";
            //    queryPoi += "AND node_lat <= '" + bounds_world_max_lat.ToString().Replace(",", ".") + "' ";
            //    queryPoi += "AND type = 'building' ORDER BY id_way, num";

            //    DataTable resultDataTableBuildings = db.ExecuteQuery(queryBuildings);

            //    long lastBuildingId = -1;
            //    OSM_Building lastBuildingCreated = new OSM_Building();

            //    foreach (DataRow tmp in resultDataTableBuildings.Rows)
            //    {
            //        long.TryParse(tmp["id_way"].ToString(), out lastBuildingId);

            //        if (lastBuildingId != lastBuildingCreated.id)
            //        {
            //            Cache_buildings.Add(lastBuildingCreated);
            //            lastBuildingCreated = new OSM_Building();
            //            lastBuildingCreated.id = lastBuildingId;
            //        }

            //        float floatLon = 0f;
            //        float.TryParse(tmp["node_lon"].ToString(), out floatLon);
            //        float floatLat = 0f;
            //        float.TryParse(tmp["node_lat"].ToString(), out floatLat);
            //        OSM_Node newNode = new OSM_Node();
            //        long.TryParse(tmp["id_node"].ToString(), out newNode.id);
            //        newNode.LON = floatLon;
            //        newNode.LAT = floatLat;

            //        lastBuildingCreated.nodes.Add(newNode);
            //    }


            //    ways = Cache_ways;
            //    buildings = Cache_buildings;

            //}
        }
        public OSM_Way WhereAmI()
        {
            OSM_Way result = currentWay;
            OSM_Node closestNodeFound = null;
            ArrayList nodesFound = new ArrayList();

            foreach (OSM_Way tmpWay in ways)
            {
                OSM_Node found = ClosestNode(GetProximityNodeList(tmpWay.nodes, CAR_position));

                if (null != found)
                {
                    if (null != closestNodeFound)
                    {
                        if ((found.LAT_proximity <= closestNodeFound.LAT_proximity) && (found.LON_proximity <= closestNodeFound.LON_proximity))
                        {
                            closestNodeFound = found;
                            result = tmpWay;
                        }
                    }
                    else
                    {
                        closestNodeFound = found;
                        result = tmpWay;
                    }
                }
            }


            return result;
        }
        private OSM_Node ClosestNode(ArrayList nodes)
        {
            OSM_Node result = null;

            foreach (OSM_Node tmpNode in nodes)
            {
                if (null != tmpNode)
                    if (null != result)
                    {
                        if ((tmpNode.LAT_proximity <= result.LAT_proximity) && (tmpNode.LON_proximity <= result.LON_proximity))
                            result = tmpNode;
                    }
                    else
                        result = tmpNode;
            }

            return result;
        }
        private ArrayList GetProximityNodeList(ArrayList nodes, PointF baseNode)
        {
            ArrayList result = new ArrayList();

            foreach (OSM_Node tmpNode in nodes)
            {
                OSM_Node clonedNode = tmpNode;

                clonedNode.LAT_proximity = baseNode.Y - clonedNode.LAT;
                clonedNode.LON_proximity = baseNode.X - clonedNode.LON;

                if (clonedNode.LAT_proximity < 0) clonedNode.LAT_proximity = clonedNode.LAT_proximity * -1;
                if (clonedNode.LON_proximity < 0) clonedNode.LON_proximity = clonedNode.LON_proximity * -1;

                result.Add(clonedNode);
            }

            return result;
        }

        //private void LoadData_OLD1()
        //{
        //    LoadingDataFormDB = true;
        //    ways.Clear();
        //    buildings.Clear();

        //    db.Connect();

        //    //***********  WAYS  **********************

        //    //SELECT way_nodes.way AS id_way, nodes.id AS id_node, num, nodes.lon, nodes.lat, way_tags.value AS name 
        //    //FROM nodes INNER JOIN way_nodes ON nodes.id = way_nodes.node INNER JOIN way_tags ON way_nodes.way = way_tags.ref WHERE nodes.lon >= '9.23189' AND nodes.lon <= '9.24274' AND nodes.lat >= '45.48772' AND nodes.lat <= '45.49401' AND  way_tags.key = 'highway' GROUP BY way_nodes.way

        //    string queryWays = "SELECT * ";
        //    queryWays += "FROM ways_full ";
        //    queryWays += "WHERE node_lon >= '" + bounds_world_min_lon.ToString().Replace(",", ".") + "' ";
        //    queryWays += "AND node_lon <= '" + bounds_world_max_lon.ToString().Replace(",", ".") + "' ";
        //    queryWays += "AND node_lat >= '" + bounds_world_min_lat.ToString().Replace(",", ".") + "' ";
        //    queryWays += "AND node_lat <= '" + bounds_world_max_lat.ToString().Replace(",", ".") + "' ";
        //    queryWays += "AND type = 'highway' ORDER BY id_way, num";

        //    DateTime StartLoadingDataFromDbTime = DateTime.Now;
        //    DataTable resultDataTableWays = db.ExecuteQuery(queryWays);
        //    LastLoadDataFromDbTime = DateTime.Now - StartLoadingDataFromDbTime;

        //    long lastWayId = -1;
        //    OSM_Way lastWayCreated = new OSM_Way();

        //    foreach (DataRow tmp in resultDataTableWays.Rows)
        //    {
        //        long.TryParse(tmp["id_way"].ToString(), out lastWayId);

        //        if (lastWayId != lastWayCreated.id)
        //        {
        //            ways.Add(lastWayCreated);
        //            lastWayCreated = new OSM_Way();
        //            lastWayCreated.id = lastWayId;
        //            lastWayCreated.name = tmp["name"].ToString();
        //        }

        //        lastWayCreated.wayType = tmp["subtype"].ToString();

        //        float floatLon = 0f;
        //        float.TryParse(tmp["node_lon"].ToString(), out floatLon);
        //        float floatLat = 0f;
        //        float.TryParse(tmp["node_lat"].ToString(), out floatLat);
        //        OSM_Node newNode = new OSM_Node();
        //        long.TryParse(tmp["id_node"].ToString(), out newNode.id);
        //        newNode.LON = floatLon;
        //        newNode.LAT = floatLat;

        //        lastWayCreated.nodes.Add(newNode);
        //    }


        //    //***********  BUILDINGS  **********************

        //    string queryBuildings = "SELECT * ";
        //    queryBuildings += "FROM ways_full ";
        //    queryBuildings += "WHERE node_lon >= '" + bounds_world_min_lon.ToString().Replace(",", ".") + "' ";
        //    queryBuildings += "AND node_lon <= '" + bounds_world_max_lon.ToString().Replace(",", ".") + "' ";
        //    queryBuildings += "AND node_lat >= '" + bounds_world_min_lat.ToString().Replace(",", ".") + "' ";
        //    queryBuildings += "AND node_lat <= '" + bounds_world_max_lat.ToString().Replace(",", ".") + "' ";
        //    queryBuildings += "AND type = 'building' ORDER BY id_way, num";

        //    DataTable resultDataTableBuildings = db.ExecuteQuery(queryBuildings);

        //    long lastBuildingId = -1;
        //    OSM_Building lastBuildingCreated = new OSM_Building();

        //    foreach (DataRow tmp in resultDataTableBuildings.Rows)
        //    {
        //        long.TryParse(tmp["id_way"].ToString(), out lastBuildingId);

        //        if (lastBuildingId != lastBuildingCreated.id)
        //        {
        //            buildings.Add(lastBuildingCreated);
        //            lastBuildingCreated = new OSM_Building();
        //            lastBuildingCreated.id = lastBuildingId;
        //        }

        //        float floatLon = 0f;
        //        float.TryParse(tmp["node_lon"].ToString(), out floatLon);
        //        float floatLat = 0f;
        //        float.TryParse(tmp["node_lat"].ToString(), out floatLat);
        //        OSM_Node newNode = new OSM_Node();
        //        long.TryParse(tmp["id_node"].ToString(), out newNode.id);
        //        newNode.LON = floatLon;
        //        newNode.LAT = floatLat;

        //        lastBuildingCreated.nodes.Add(newNode);
        //    }

        //    ToRepaint = true;
        //    LoadingDataFormDB = false;
        //}

        public void Zoom_IN()
        {
            //bounds_world_min_lat += zoom_factor_lat;
            //bounds_world_max_lat -= zoom_factor_lat;
            //bounds_world_min_lon += zoom_factor_lon;
            //bounds_world_max_lon -= zoom_factor_lon;
            //LoadData();

            if (((bounds_step_lat / 2) != 0) && ((bounds_step_lon / 2) != 0))
            {
                bounds_step_lat = bounds_step_lat / 2;
                bounds_step_lon = bounds_step_lon / 2;
            }
        }
        public void Zoom_OUT()
        {
            //bounds_world_min_lat -= zoom_factor_lat;
            //bounds_world_max_lat += zoom_factor_lat;
            //bounds_world_min_lon -= zoom_factor_lon;
            //bounds_world_max_lon += zoom_factor_lon;
            //LoadData();

            bounds_step_lat = bounds_step_lat * 2;
            bounds_step_lon = bounds_step_lon * 2;

        }
        //public void Move_LEFT()
        //{
        //    bounds_world_min_lon -= zoom_factor_lon;
        //    bounds_world_max_lon -= zoom_factor_lon;
        //    LoadData();
        //}
        //public void Move_RIGHT()
        //{
        //    bounds_world_min_lon += zoom_factor_lon;
        //    bounds_world_max_lon += zoom_factor_lon;
        //    LoadData();
        //}
        //public void Move_UP()
        //{
        //    bounds_world_min_lat += zoom_factor_lat;
        //    bounds_world_max_lat += zoom_factor_lat;
        //    LoadData();
        //}
        //public void Move_DOWN()
        //{
        //    bounds_world_min_lat -= zoom_factor_lat;
        //    bounds_world_max_lat -= zoom_factor_lat;
        //    LoadData();
        //}
    }


    class OSM_Way
    {
        public long id = -1;
        public string name = "";
        public string wayType = "unclassified";
        public ArrayList nodes = new ArrayList();
    }

    class OSM_Building
    {
        public long id = -1;
        public ArrayList nodes = new ArrayList();
    }

    //For icons: http://wiki.openstreetmap.org/wiki/Map_Features
    class OSM_Poi
    {
        public long id = -1;
        public OSM_Node position = null;
    }

    class OSM_Node
    {
        public long id = -1;
        public float LON = 0f;
        public float LAT = 0f;
        public float LON_proximity = 0f;
        public float LAT_proximity = 0f;
    }
}
